﻿#region Legal

// Jimmy Zimmerman
// Team Mongoose
//
// END USER LICENSE AGREEMENT
// IMPORTANT - READ THIS CAREFULLY:  This End User License Agreement is a legal agreement
// between you (either an individual, business entity, currently active identity of an
// individual with Multiple Personality Disorder, octopus overlord, or otherworldly entity),
// and Team Mongoose, for the enclosed, components.
//
// By reading this document and/or installing this product, you agree to be bound by the
// terms of this EULA.
//
// Team Mongoose owns all copyright, trade secret, trademark, trade wars,
// patent, portent, and potential rights to these components.  Team Mongoose
// grants you the right to deploy the enclosed components.
//
// If you agree to the terms of this EULA, a license to use these components is granted to you.
//
// If you should happen to benefit from the enclosed components, then you are legally and
// contractually bound to thank us for it. Send your regards to jimmyzimms@hotmail.com.
//
// OTHER RIGHTS AND LIMITATONS
// You may not reverse-engineer, decompile, decode, disassemble, psycho-analyze, or otherwise
// attempt to find hidden meanings between the lines of this EULA; unless, of course, you should
// happen to find some, and they are funny.
//
// You may not translate this EULA or any part of the components into Ancient Sumerian.
//
// THESE COMPONENTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. ANY USE OF THE COMPONENTS
// CONTAINED IS AT YOUR OWN RISK. TEAM MONGOOSE DISCLAIM ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, WITH RESPECT TO THE ACCURRACY AND CORRECTNESS OF THE COMPONENTS CONTAINED
// HEREIN. TEAM MONGOOSE DOES NOT WARRANT THAT THE COMPONENTS ARE FLAWLESS.
//
// REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED
// PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
// * REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE
// * REDISTRIBUTIONS IN BINARY FORM MUST NOTE THE USE OF THE COMPONENT IN DOCUMENTATION AND/OR
//   OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
// * NEITHER THE NAME OF TEAM MONGOOSE MAY BE USED TO ENDORES OR PROMOTE PRODUCTS
//   DERIVED FROM THIS SOFTWARE WITHOUT SPECIFIC PRIOR WRITTEN PERMISSION.
//
// IN NO EVENT SHALL TEAM MONGOOSE BE HELD LIABLE FOR INCIDENTAL, SPECIAL, INDIRECT,
// INCONSEQUENTIAL, UNBELIEVABLE, EXAGGERATED, VERBOSE, OR TYPICAL DAMAGES INCURRED WHILE USING
// THE ENCLOSED COMPONENTS.
//
// OUR STUFF ALWAYS WORKS - SOMETIMES.

#endregion

using System.ComponentModel;
using System.Data.Common;
using System.Linq;
using System.Text;

// TODO: add all resource strings

namespace System.Data.MdxClient
{
    /// <summary>
    /// Represents a SQL Server Analyis Services connection string builder.
    /// </summary>
    public class MdxConnectionStringBuilder : DbConnectionStringBuilder
    {
        #region Fields

        private static readonly Collections.ObjectModel.ReadOnlyCollection<String> ValidKeywords =
            new Collections.ObjectModel.ReadOnlyCollection<String>(new[]
                                                                       {
                                                                           "Timeout",
                                                                           "Connect Timeout",
                                                                           "Data Source",
                                                                           "Initial Catalog",
                                                                           /*"Password",
                                                                           "PWD",
                                                                           "Data Source Location",
                                                                           "UserID",
                                                                           "User ID",
                                                                           "Database",
                                                                           "Catalog",
                                                                           "ConnectTo",
                                                                           "Encryption Password",
                                                                           "Extended Properties",
                                                                           "Location",
                                                                           "SessionID",
                                                                           "ClientProcessID",
                                                                           "SSPropInitAppName",
                                                                           "ProtectionLevel",
                                                                           "AutoSyncPeriod",*/
                                                                           "Integrated Security"/*,
                                                                           "Protocol Format",
                                                                           "Impersonation Level",
                                                                           "SSPI",
                                                                           "UseExistingFile",
                                                                           "Character Encoding",
                                                                           "Use Encryption For Data",
                                                                           "Packet Size",
                                                                           "LocaleIdentifier",
                                                                           "Persist Security Info",
                                                                           "Transport Compression",
                                                                           "Compression Level",
                                                                           "Disable Prefetch Facts"*/
                                                                       });

        private string applicationName;
        private int? commandTimeout;
        private int? connectionTimeout;
        private string dataSource;
        private string userId;
        private string password;
        private string initialCatalog;
        private int? autoSyncPeriod;
        private string connectTo;
        private ProtectionLevel protectionLevel = ProtectionLevel.None;
        private IntegratedSecurity integratedSecurity = IntegratedSecurity.Unspecified;
        private ProtocolFormat protocolFormat = ProtocolFormat.Default;
        private string encryptionPassword;
        private ImpersonationLevel impersonationLevel = ImpersonationLevel.Impersonate;
        private SecurityPackage sspi = SecurityPackage.Negotiate;
        private bool useExistingFile;
        private Encoding encoding = Encoding.UTF8;
        private bool useEncryptionForData;
        private int packetSize = 0x1000;
        private string extendedProperties;
        private short? lcid;
        private string location;
        private bool persistSecurityInfo;
        private string sessionId;
        private string clientProcessId;
        private TransportCompression transportCompression = TransportCompression.Default;
        private int? compressionLevel;
        private bool disablePrefetchFacts;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the name of the application that is to be associated with a connection.
        /// </summary>
        /// <value>The application associated with the connection.</value>
        [DisplayName( "SspropInitAppName" )]
        [SRDescription( "ConnectionStringApplicationName" )]
        [SRCategory( "CategoryContext" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ApplicationName
        {
            get
            {
                return this.applicationName;
            }
            set
            {
                this.SetValue( "SspropInitAppName", value );
                this.applicationName = value;
            }
        }

        /// <summary>
        /// Gets or sets the time to wait for a command to run before an error is generated.
        /// </summary>
        /// <value>The timeout period for a command.</value>
        [DisplayName( "Timeout" )]
        [SRDescription( "ConnectionStringCommandTimeout" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public int CommandTimeout
        {
            get
            {
                return this.commandTimeout ?? 30;
            }
            set
            {
                GuardValue( value < 0, "Timeout" );
                this.SetValue( "Timeout", value );
                this.commandTimeout = value;
            }
        }

        /// <summary>
        /// Gets or sets the time to wait for a connection to be established before an error is generated.
        /// </summary>
        /// <value>The timeout period for a connection.</value>
        [DisplayName( "Connect Timeout" )]
        [SRDescription( "ConnectionStringConnectTimeout" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public int ConnectTimeout
        {
            get
            {
                return this.connectionTimeout ?? 15;
            }
            set
            {
                GuardValue( value < 0, "Connect Timeout" );
                this.SetValue( "Connect Timeout", value );
                this.connectionTimeout = value;
            }
        }

        /// <summary>
        /// Gets or sets the instance or local cube (.cub) file to establish a connection to.
        /// </summary>
        /// <value>The instance or local cube to connection to.</value>
        [DisplayName( "Data Source" )]
        [SRDescription( "ConnectionStringDataSource" )]
        [SRCategory( "CategorySource" )]
        [RefreshProperties( RefreshProperties.All )]
        public string DataSource
        {
            get
            {
                return this.dataSource;
            }
            set
            {
                this.SetValue( "Data Source", value );
                this.dataSource = value;
            }
        }

        /// <summary>
        /// Gets or sets the user identifier or name when connecting.
        /// </summary>
        /// <value>The connection user identifier.</value>
        [DisplayName( "User ID" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringUserID" )]
        [RefreshProperties( RefreshProperties.All )]
        public string UserName
        {
            get
            {
                return this.userId;
            }
            set
            {
                this.SetValue( "User ID", value );
                this.userId = value;
            }
        }

        /// <summary>
        /// Gets or sets the password that is used to establish a connection with the specified user name.
        /// </summary>
        /// <value>The connection password.</value>
        [DisplayName( "Password" )]
        [SRDescription( "ConnectionStringPassword" )]
        [SRCategory( "CategorySecurity" )]
        [RefreshProperties( RefreshProperties.All )]
        [PasswordPropertyText( true )]
        public string Password
        {
            get
            {
                return this.password;
            }
            set
            {
                this.SetValue( "Password", value );
                this.password = value;
            }
        }

        /// <summary>
        /// Gets or sets the database to which to connect to.
        /// </summary>
        /// <value>The database to connect to.</value>
        [DisplayName( "Initial Catalog" )]
        [SRDescription( "ConnectionStringInitialCatalog" )]
        [SRCategory( "CategorySource" )]
        [RefreshProperties( RefreshProperties.All )]
        public string InitialCatalog
        {
            get
            {
                return this.initialCatalog;
            }
            set
            {
                this.SetValue( "Initial Catalog", value );
                this.initialCatalog = value;
            }
        }

        /// <summary>
        /// Gets or sets the level of protection that the provider uses to sign or encrypt the connection.
        /// </summary>
        /// <value>One of the <see cref="ProtectionLevel"/> values.  The default value is <see cref="T:ProtectionLevel.None"/>.</value>
        [DisplayName( "ProtectionLevel" )]
        [SRDescription( "ConnectionStringProtectionLevel" )]
        [SRCategory( "CategorySecurity" )]
        [RefreshProperties( RefreshProperties.All )]
        public ProtectionLevel ProtectionLevel
        {
            get
            {
                return this.protectionLevel;
            }
            set
            {
                this.SetValue( "ProtectionLevel", value );
                this.protectionLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets the time, in milliseconds, that elapses before objects automatically synchronize with the server.
        /// </summary>
        /// <value>The automatic synchronization period in milliseconds.</value>
        [DisplayName( "AutoSyncPeriod " )]
        [SRDescription( "ConnectionStringAutoSyncPeriod" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public int AutoSyncPeriod
        {
            get
            {
                return this.autoSyncPeriod ?? 1000;
            }
            set
            {
                GuardValue( value < 0, "AutoSyncPeriod" );
                this.SetValue( "AutoSyncPeriod", value );
                this.autoSyncPeriod = value;
            }
        }

        /// <summary>
        /// Gets or sets the method that used to connect to the server.
        /// </summary>
        /// <value>The method used to connect to the server.</value>
        /// <remarks>If set to 8.0, the connection uses in-process XML for Analysis to connect to the server.
        /// If set to 9.0, the connection uses XML for Analysis. If set to Default, the connection first tries
        /// XML for Analysis, and then tries to use in-process XML for Analysis.</remarks>
        [DisplayName( "ConnectTo" )]
        [SRDescription( "ConnectionStringConnectTo" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ConnectTo
        {
            get
            {
                return this.connectTo;
            }
            set
            {
                this.SetValue( "ConnectTo", value );
                this.connectTo = value;
            }
        }

        /// <summary>
        /// Gets or sets the control access to use.
        /// </summary>
        /// <value>One of the <see cref="IntegratedSecurity"/> values.</value>
        /// <remarks>If set to <see cref="T:IntegratedSecurity.Sspi"/>, an SSPI supported security package is used
        /// for user authentication. If set to <see cref="T:IntegratedSecurity.Basic"/>, the <see cref="P:UserName"/>
        /// and <see cref="P:Password"/> settings are required. An HTTP connection can use only the setting of
        /// <see cref="T:IntegratedSecurity.Basic"/>.</remarks>
        [DisplayName( "Integrated Security" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringIntegratedSecurity" )]
        [RefreshProperties( RefreshProperties.All )]
        public IntegratedSecurity IntegratedSecurity
        {
            get
            {
                return this.integratedSecurity;
            }
            set
            {
                this.SetValue( "Integrated Security", value );
                this.integratedSecurity = value;
            }
        }

        /// <summary>
        /// Gets or sets the format of the XML sent to the server.
        /// </summary>
        /// <value>One of the <see cref="ProtocolFormat"/> values.</value>
        [DisplayName( "Protocol Format" )]
        [SRCategory( "CategoryInitialization" )]
        [SRDescription( "ConnectionStringProtocolFormat" )]
        [RefreshProperties( RefreshProperties.All )]
        public ProtocolFormat ProtocolFormat
        {
            get
            {
                return this.protocolFormat;
            }
            set
            {
                this.SetValue( "Protocol Format", value );
                this.protocolFormat = value;
            }
        }

        /// <summary>
        /// Gets or sets the password used to decrypt local cubes.
        /// </summary>
        /// <value>The local cube decryption password.</value>
        [DisplayName( "Encryption Password" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringEncryptionPassword" )]
        [RefreshProperties( RefreshProperties.All )]
        public string EncryptionPassword
        {
            get
            {
                return this.encryptionPassword;
            }
            set
            {
                this.SetValue( "Encryption Password", value );
                this.encryptionPassword = value;
            }
        }

        /// <summary>
        /// Gets or sets the level of impersonation that the server is allowed to use when impersonating the client.
        /// </summary>
        /// <value>One of the <see cref="ImpersonationLevel"/> values.  The default value is
        /// <see cref="T:ImpersonationLevel.Impersonate"/>.</value>
        [DisplayName( "Impersonation Level" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringImpersonationLevel" )]
        [RefreshProperties( RefreshProperties.All )]
        public ImpersonationLevel ImpersonationLevel
        {
            get
            {
                return this.impersonationLevel;
            }
            set
            {
                this.SetValue( "Impersonation Level", value );
                this.impersonationLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets the security package to use for user authentication.
        /// </summary>
        /// <value>One of the <see cref="SecurityPackage"/> values.  The default value is
        /// <see cref="T:SecurityPackage.Negotiate"/>.</value>
        [DisplayName( "SSPI" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringSspi" )]
        [RefreshProperties( RefreshProperties.All )]
        public SecurityPackage Sspi
        {
            get
            {
                return this.sspi;
            }
            set
            {
                this.SetValue( "SSPI", value );
                this.sspi = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to use or overwrite the existing file.
        /// </summary>
        /// <value>If set to true, the local file must already exist, and the cube is either created if the
        /// cube does not exist, or used if the cube does exist. If set to false, the existing local cube is
        /// overwritten. The default is false.</value>
        [DisplayName( "UseExistingFile" )]
        [SRCategory( "CategoryInitialization" )]
        [SRDescription( "ConnectionStringUseExistingFile" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool UseExistingFile
        {
            get
            {
                return this.useExistingFile;
            }
            set
            {
                this.SetValue( "UseExistingFile", value );
                this.useExistingFile = value;
            }
        }

        /// <summary>
        /// Gets or sets how characters are encoded.
        /// </summary>
        /// <value>An <see cref="Encoding"/> object. The default value is <see cref="T:Encoding.UTF8"/>.</value>
        [DisplayName( "Character Encoding" )]
        [SRCategory( "CategoryData" )]
        [SRDescription( "ConnectionStringCharacterEncoding" )]
        [RefreshProperties( RefreshProperties.All )]
        // NOTE: a TypeConverter is required for this to be browsable
        [Browsable( false )]
        public Encoding CharacterEncoding
        {
            get
            {
                return this.encoding;
            }
            set
            {
                this.SetValue( "Character Encoding", value );
                this.encoding = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether encryption is used between the client and server.
        /// </summary>
        /// <value>If set to true, all data sent between the client and server is encrypted with Secure
        /// Sockets Layer (SSL) encryption, provided that the server has a certificate installed.</value>
        [DisplayName( "Use Encryption for Data" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringUseEncryptionForData" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool UseEncryptionForData
        {
            get
            {
                return this.useEncryptionForData;
            }
            set
            {
                this.SetValue( "Use Encryption for Data", value );
                this.useEncryptionForData = value;
            }
        }

        /// <summary>
        /// Gets or sets the packet size, in bytes, of network communications between client and server.
        /// </summary>
        /// <value>The value must between 512 and 32767. The default is 4096.</value>
        [DisplayName( "Packet Size" )]
        [SRDescription( "ConnectionStringPacketSize" )]
        [SRCategory( "CategoryData" )]
        [RefreshProperties( RefreshProperties.All )]
        public int PacketSize
        {
            get
            {
                return this.packetSize;
            }
            set
            {
                GuardValue( value < 0x200 || value > 0x8000, "Packet Size" );
                this.SetValue( "Packet Size", value );
                this.packetSize = value;
            }
        }

        /// <summary>
        /// Gets or sets the connection string properties that are to override the connection string in which extended properties is contained.
        /// </summary>
        /// <value>The connection string extended properties.</value>
        /// <remarks>This property is itself a connection string, the properties of which override the connection string in which
        /// Extended Properties is contained. Extended Properties can contain another Extended Properties, the properties of which
        /// maintain precedence. Extended Properties supports unlimited nesting.</remarks>
        [DisplayName( "Extended Properties" )]
        [SRCategory( "CategoryAdvanced" )]
        [SRDescription( "ConnectionStringExtendedProperties" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ExtendedProperties
        {
            get
            {
                return this.extendedProperties;
            }
            set
            {
                this.SetValue( "Extended Properties", value );
                this.extendedProperties = value;
            }
        }

        /// <summary>
        /// Gets or sets the preferred locale identifier (LCID) for the client application.
        /// </summary>
        /// <value>The ISO locale identifer.</value>
        [DisplayName( "LocaleIdentifier" )]
        [SRDescription( "ConnectionStringLocaleIdentifier" )]
        [SRCategory( "CategoryData" )]
        [RefreshProperties( RefreshProperties.All )]
        public short LocaleIdentifier
        {
            get
            {
                return this.lcid ?? 1033;
            }
            set
            {
                this.SetValue( "LocaleIdentifier", value );
                this.lcid = value;
            }
        }

        /// <summary>
        /// Gets or sets the name of the server.
        /// </summary>
        /// <value>The name of the server.</value>
        [DisplayName( "Location" )]
        [SRCategory( "CategorySource" )]
        [SRDescription( "ConnectionStringLocation" )]
        [RefreshProperties( RefreshProperties.All )]
        public string Location
        {
            get
            {
                return this.location;
            }
            set
            {
                this.SetValue( "Location", value );
                this.location = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether security information will be persisted.
        /// </summary>
        /// <value>If set to true, security-related information can be obtained from the connection
        /// after the connection has been opened.</value>
        [DisplayName( "Persist Security Info" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringPersistSecurityInfo" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool PersistSecurityInfo
        {
            get
            {
                return this.persistSecurityInfo;
            }
            set
            {
                this.SetValue( "Persist Security Info", value );
                this.persistSecurityInfo = value;
            }
        }

        /// <summary>
        /// Gets or sets the session identifier that the connection will use.
        /// </summary>
        /// <value>The session identifier for the connection.</value>
        /// <remarks>If the session identifier does not match an existing session, an error will be raised.</remarks>
        [DisplayName( "SessionID" )]
        [SRDescription( "ConnectionStringSessionID" )]
        [SRCategory( "CategoryContext" )]
        [RefreshProperties( RefreshProperties.All )]
        public string SessionId
        {
            get
            {
                return this.sessionId;
            }
            set
            {
                this.SetValue( "SessionID", value );
                this.sessionId = value;
            }
        }

        /// <summary>
        /// Gets or sets whether the connection will communicate with compression.
        /// </summary>
        /// <value>One of the <see cref="TransportCompression"/> values.</value>
        /// <remarks>If set to <see cref="T:TransportCompression.None"/>, no compression will be used.
        /// If set to <see cref="T:TransportCompression.Compressed"/>, communications may be compressed
        /// to reduce packet size. The <see cref="T:TransportCompression.Gzip"/> setting compresses HTTP connections.
        /// If set to <see cref="T:TransportCompression.Default"/>, <see cref="T:TransportCompression.Gzip"/> will
        /// be used over an HTTP connection; otherwise, no compression will be used.</remarks>
        [DisplayName( "Transport Compression" )]
        [SRDescription( "ConnectionStringTransportCompression" )]
        [SRCategory( "CategoryAdvanced" )]
        [RefreshProperties( RefreshProperties.All )]
        public TransportCompression TransportCompression
        {
            get
            {
                return this.transportCompression;
            }
            set
            {
                this.SetValue( "Transport Compression", value );
                this.transportCompression = value;
            }
        }

        /// <summary>
        /// Gets or sets the compression level.
        /// </summary>
        /// <value>The compression level, from 0 to 9.</value>
        /// <remarks>The greater the value, the higher the compression.</remarks>
        [DisplayName( "Compression Level" )]
        [SRDescription( "ConnectionStringCompressionLevel" )]
        [SRCategory( "CategoryAdvanced" )]
        [RefreshProperties( RefreshProperties.All )]
        public int CompressionLevel
        {
            get
            {
                return this.compressionLevel ?? 0;
            }
            set
            {
                GuardValue( value < 0 || value > 9, "Compression Level" );
                this.SetValue( "Compression Level", value );
                this.compressionLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets the process identifier of the application that is to be associated with this connection.
        /// </summary>
        /// <value>The client process identifier of the application.</value>
        [DisplayName( "ClientProcessID" )]
        [SRDescription( "ConnectionStringClientProcessID" )]
        [SRCategory( "CategoryContext" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ClientProcessId
        {
            get
            {
                return this.clientProcessId;
            }
            set
            {
                this.SetValue( "ClientProcessID", value );
                this.clientProcessId = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the engine stops trying to pre-fetch values for the duration of the session.
        /// </summary>
        /// <value>When set to true, the engine stops trying to pre-fetch values for the duration of the session.
        /// The default value is false.</value>
        [DisplayName( "Disable Prefetch Facts" )]
        [SRCategory( "CategoryAdvanced" )]
        [SRDescription( "ConnectionStringDisablePrefetchFacts" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool DisablePrefetchFacts
        {
            get
            {
                return this.disablePrefetchFacts;
            }
            set
            {
                this.SetValue( "Disable Prefetch Facts", value );
                this.disablePrefetchFacts = value;
            }
        }

        #endregion

        #region Methods

        private static void GuardValue( bool throwError, string key )
        {
            if ( throwError )
                throw new ArgumentException( string.Format( null, SR.InvalidConnectionOptionValue, key ) );
        }

        private void SetValue( string keyword, ProtectionLevel level )
        {
            var value = string.Empty;

            switch ( level )
            {
                case ProtectionLevel.ConnectOnly:
                    {
                        value = "CONNECT";
                        break;
                    }
                case ProtectionLevel.Encryption:
                    {
                        value = "PKT PRIVACY";
                        break;
                    }
                case ProtectionLevel.None:
                    {
                        value = "NONE";
                        break;
                    }
                case ProtectionLevel.Signing:
                    {
                        value = "PKT INTEGRITY";
                        break;
                    }
            }

            base[keyword] = value;
        }

        private void SetValue( string keyword, Encoding value )
        {
            if ( value == null )
                throw new ArgumentNullException( "value" );

            base[keyword] = value.BodyName;
        }

        private void SetValue<TValue>( string keyword, TValue value )
        {
            if ( !( value is ValueType ) && ( value == null ) )
                throw new ArgumentNullException( "value" );

            base[keyword] = value.ToString();
        }

        private static TEnum ConvertToEnum<TEnum>( object value )
        {
            if ( value == null )
                return default( TEnum );

            return (TEnum) Enum.Parse( typeof( TEnum ), value.ToString(), true );
        }

        private static Encoding ConvertToEncoding( object value )
        {
            if ( value == null )
                return Encoding.UTF8;

            var encoding = value as Encoding;

            if ( encoding != null )
                return encoding;

            encoding = Encoding.GetEncoding( value.ToString() );
            return encoding;
        }

        private object GetPropertyValue( string keyword )
        {
            if ( string.IsNullOrEmpty( keyword ) )
                throw new ArgumentException( string.Format( null, SR.KeywordNotSupported, keyword ) );

            var value = keyword.ToUpperInvariant();

            switch ( value )
            {
                case "TIMEOUT":
                    return this.CommandTimeout;
                case "CONNECT TIMEOUT":
                    return this.ConnectTimeout;
                case "PASSWORD":
                case "PWD":
                    return this.Password;
                case "DATA SOURCE":
                case "DATASOURCELOCATION":
                    return this.DataSource;
                case "USERNAME":
                case "USERID":
                case "USER ID":
                    return this.UserName;
                case "DATABASE":
                case "CATALOG":
                case "INITIAL CATALOG":
                    return this.InitialCatalog;
                case "CONNECTTO":
                    return this.ConnectTo;
                case "ENCRYPTION PASSWORD":
                    return this.EncryptionPassword;
                case "EXTENDED PROPERTIES":
                    return this.ExtendedProperties;
                case "LOCATION":
                    return this.Location;
                case "SESSIONID":
                    return this.SessionId;
                case "CLIENTPROCESSID":
                    return this.ClientProcessId;
                case "SSPROPINITAPPNAME":
                    return this.ApplicationName;
                case "PROTECTIONLEVEL":
                    return this.ProtectionLevel;
                case "AUTOSYNCPERIOD":
                    return this.AutoSyncPeriod;
                case "INTEGRATED SECURITY":
                    return this.IntegratedSecurity;
                case "PROTOCOL FORMAT":
                    return this.ProtocolFormat;
                case "IMPERSONATION LEVEL":
                    return this.ImpersonationLevel;
                case "SSPI":
                    return this.Sspi;
                case "USEEXISTINGFILE":
                    return this.UseEncryptionForData;
                case "CHARACTER ENCODING":
                    return this.CharacterEncoding;
                case "USE ENCRYPTION FOR DATA":
                    return this.UseEncryptionForData;
                case "PACKET SIZE":
                    return this.PacketSize;
                case "LocaleIdentifier":
                    return this.LocaleIdentifier;
                case "PERSIST SECURITY INFO":
                    return this.PersistSecurityInfo;
                case "TRANSPORT COMPRESSION":
                    return this.TransportCompression;
                case "COMPRESSION LEVEL":
                    return this.CompressionLevel;
                case "DISABLE PREFETCH FACTS":
                    return this.DisablePrefetchFacts;
            }

            throw new ArgumentException( string.Format( null, SR.KeywordNotSupported, keyword ) );
        }

        private void SetPropertyValue( string keyword, object value )
        {
            if ( string.IsNullOrEmpty( keyword ) )
                throw new ArgumentException( string.Format( null, SR.KeywordNotSupported, keyword ) );

            var key = keyword.ToUpperInvariant();

            switch (key)
            {
                case "TIMEOUT":
                    this.CommandTimeout = Convert.ToInt32(value);
                    break;
                case "CONNECT TIMEOUT":
                    this.ConnectTimeout = Convert.ToInt32(value);
                    break;
                case "PASSWORD":
                case "PWD":
                    this.Password = value as string;
                    break;
                case "DATA SOURCE":
                case "DATASOURCELOCATION":
                    this.DataSource = value as string;
                    break;
                case "USERNAME":
                case "USERID":
                case "USER ID":
                    this.UserName = value as string;
                    break;
                case "DATABASE":
                case "CATALOG":
                case "INITIAL CATALOG":
                    this.InitialCatalog = value as string;
                    break;
                case "CONNECTTO":
                    this.ConnectTo = value as string;
                    break;
                case "ENCRYPTION PASSWORD":
                    this.EncryptionPassword = value as string;
                    break;
                case "EXTENDED PROPERTIES":
                    this.ExtendedProperties = value as string;
                    break;
                case "LOCATION":
                    this.Location = value as string;
                    break;
                case "SESSIONID":
                    this.SessionId = value as string;
                    break;
                case "CLIENTPROCESSID":
                    this.ClientProcessId = value as string;
                    break;
                case "SSPROPINITAPPNAME":
                    this.ApplicationName = value as string;
                    break;
                case "PROTECTIONLEVEL":
                    this.ProtectionLevel = ConvertToEnum<ProtectionLevel>(value);
                    break;
                case "AUTOSYNCPERIOD":
                    this.AutoSyncPeriod = Convert.ToInt32(value);
                    break;
                case "INTEGRATED SECURITY":
                    this.IntegratedSecurity = ConvertToEnum<IntegratedSecurity>(value);
                    break;
                case "PROTOCOL FORMAT":
                    this.ProtocolFormat = ConvertToEnum<ProtocolFormat>(value);
                    break;
                case "IMPERSONATION LEVEL":
                    this.ImpersonationLevel = ConvertToEnum<ImpersonationLevel>(value);
                    break;
                case "SSPI":
                    this.Sspi = ConvertToEnum<SecurityPackage>(value);
                    break;
                case "USEEXISTINGFILE":
                    this.UseEncryptionForData = Convert.ToBoolean(value);
                    break;
                case "CHARACTER ENCODING":
                    this.CharacterEncoding = ConvertToEncoding(value);
                    break;
                case "USE ENCRYPTION FOR DATA":
                    this.UseEncryptionForData = Convert.ToBoolean(value);
                    break;
                case "PACKET SIZE":
                    this.PacketSize = Convert.ToInt32(value);
                    break;
                case "LOCALEIDENTIFIER":
                    this.LocaleIdentifier = Convert.ToInt16(value);
                    break;
                case "PERSIST SECURITY INFO":
                    this.PersistSecurityInfo = Convert.ToBoolean(value);
                    break;
                case "TRANSPORT COMPRESSION":
                    this.TransportCompression = ConvertToEnum<TransportCompression>(value);
                    break;
                case "COMPRESSION LEVEL":
                    this.CompressionLevel = Convert.ToInt32(value);
                    break;
                case "DISABLE PREFETCH FACTS":
                    this.DisablePrefetchFacts = Convert.ToBoolean(value);
                    break;
                default:
                    throw new ArgumentException(string.Format(null, SR.KeywordNotSupported, keyword));
            }
        }

        #endregion

        #region Overrides

        public override Boolean ContainsKey(string keyword)
        {
            var result = ValidKeywords.Any(item => item.Equals(keyword, StringComparison.OrdinalIgnoreCase));
            return result;
        }

        public override Boolean TryGetValue(string keyword, out object value)
        {
            if (!this.ContainsKey(keyword))
            {
                value = null;
                return false;
            }

            value = this.GetPropertyValue(keyword);
            return (value != null);
        }

        /// <summary>
        /// Returns a value indicating whether the specified keyword should be serialized into the connection string.
        /// </summary>
        /// <param name="keyword">The keyword to be evaluated.</param>
        /// <returns>True if the keyword should be serialized into the connection string; otherwise, false.</returns>
        public override Boolean ShouldSerialize( string keyword )
        {
            if ( string.IsNullOrEmpty( keyword ) )
                return false;

            var value = keyword.ToUpperInvariant();

            switch ( value )
            {
                case "TIMEOUT":
                    return this.commandTimeout.HasValue;
                case "CONNECT TIMEOUT":
                    return this.connectionTimeout.HasValue;
                case "PASSWORD":
                case "PWD":
                    return true;
                case "DATA SOURCE":
                case "DATASOURCELOCATION":
                case "USERNAME":
                case "USERID":
                case "USER ID":
                case "DATABASE":
                case "CATALOG":
                case "INITIAL CATALOG":
                case "CONNECTTO":
                case "ENCRYPTION PASSWORD":
                case "EXTENDED PROPERTIES":
                case "LOCATION":
                case "SESSIONID":
                case "CLIENTPROCESSID":
                case "SSPROPINITAPPNAME":
                    return !string.IsNullOrEmpty( base[keyword] as string );
                case "PROTECTIONLEVEL":
                    return this.ProtectionLevel != ProtectionLevel.None;
                case "AUTOSYNCPERIOD":
                    return this.autoSyncPeriod.HasValue;
                case "INTEGRATED SECURITY":
                    return this.IntegratedSecurity != IntegratedSecurity.Unspecified;
                case "PROTOCOL FORMAT":
                    return this.ProtocolFormat != ProtocolFormat.Default;
                case "IMPERSONATION LEVEL":
                    return this.ImpersonationLevel != ImpersonationLevel.Impersonate;
                case "SSPI":
                    return this.Sspi != SecurityPackage.Negotiate;
                case "USEEXISTINGFILE":
                    return this.UseEncryptionForData;
                case "CHARACTER ENCODING":
                    return !Encoding.UTF8.Equals( this.CharacterEncoding );
                case "USE ENCRYPTION FOR DATA":
                    return this.UseEncryptionForData;
                case "PACKET SIZE":
                    return this.PacketSize != 0x1000;
                case "LocaleIdentifier":
                    return this.lcid.HasValue;
                case "PERSIST SECURITY INFO":
                    return this.PersistSecurityInfo;
                case "TRANSPORT COMPRESSION":
                    return this.TransportCompression != TransportCompression.Default;
                case "COMPRESSION LEVEL":
                    return this.compressionLevel.HasValue;
                case "DISABLE PREFETCH FACTS":
                    return this.DisablePrefetchFacts;
            }

            return false;
        }

        /// <summary>
        /// Gets or sets the connection string key/value pair.
        /// </summary>
        /// <param name="keyword">The keyword to set.</param>
        /// <returns>The value for the specified keyword.</returns>
        public override object this[string keyword]
        {
            get
            {
                return this.GetPropertyValue( keyword );   
            }
            set
            {
                this.SetPropertyValue( keyword, value );
            }
        }

        public override Collections.ICollection Keys
        {
            get { return ValidKeywords; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnectionStringBuilder"/> class.
        /// </summary>
        public MdxConnectionStringBuilder()
        {
        }

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnectionStringBuilder"/> class.
        /// </summary>
        /// <param name="connectionString">The base connection string for the builder.</param>
        public MdxConnectionStringBuilder( string connectionString )
        {
            this.ConnectionString = connectionString;
        }

        #endregion
    }
}
